#vertexshader
void main()
{
	gl_TexCoord[0] = gl_MultiTexCoord0;
	gl_TexCoord[1] = vec4(gl_Normal, 1);
	gl_Position = ftransform();
}

#fragmentshader
const int numballs = 3;
uniform vec4 ball[numballs];
uniform float threshold;

float Eval(int c, vec3 p)
{
	return ball[c].w/length(ball[c].xyz-p);
}

float EvalAll(vec3 p)
{
	float s = 0.0;
	for(int c = 0; c < numballs; c++)
		s += Eval(c, p);
	return s-threshold;
}


vec4 RayCast(vec3 raypos, vec3 raydir)
{
	float raytime = 0.0;

	vec3 intrpos;

	float far = 200000.0; // "farplane"
	float near = 0.0; // nearplane
	int numoutside = 0;
	for(int c = 0; c < numballs; c++)
	{
		vec3 sp = (ball[c].xyz-raypos);
		float t = dot(raydir, sp);
		vec3 closest = raypos+raydir*t;

		float m = EvalAll(closest);
		if(m < 0.0)
			numoutside += 1;
		else if(far > t)
			far = t;
	}

	// bail if we won't hit any balls
	if(numoutside == numballs)
	{
		return vec4(0.0, 0.0, 0.0, 0.0);
	}
	else
	{
		// find intersection point
		for(int i = 0; i < 10; i++)
		{
			raytime = mix(near, far, 0.5);
			intrpos = raypos+raydir*raytime;
			float k = EvalAll(intrpos);
	
			float g = step(k, 0.0);
			near = mix(near, raytime, g);
			far = mix(far, raytime, 1.0-g);
		}
	
		intrpos = raypos+raydir*raytime;
		return vec4(intrpos.xyz, 1);
	}
}

vec3 GetNormal(vec3 p)
{
	vec3 m = vec3(0.0, 0.0, 0.0);

	for(int i = 0; i < numballs; i++)
	{
		vec3 dp = (p-ball[i].xyz);
		float b = dot(dp, dp);
		m += -2.0*dp/(b*b);
	}

	return normalize(m);
}

vec4 CalculateLight(vec3 pos, vec3 normal, vec3 raydir)
{
	// setup light sources (UGLY!)
	vec3 light[2];
	light[0] = normalize(vec3(-0.5, -0.5, -1.0));
	light[1] = normalize(vec3(0.5, 0.5, 1.0));
	vec4 lightcolor[2];
	lightcolor[0] = vec4(0.05, 0.05, 0.15,  0.6);
	lightcolor[1] = vec4(0.25, 0.15, 0.05, 0.1);

	// calculate light
	vec3 c = vec3(0);
	for(int i = 0; i < 2; i++)
	{
		float d = max(dot(normal, light[i].xyz), 0.0);
		float sd = max(dot(reflect(light[i].xyz, normal), raydir*-1), 0.0);
		c += d*lightcolor[i].xyz + pow(sd, 32)*vec3(1.0,1.0,1.0)*lightcolor[i].w;
	}
	c += vec3(0.05,0.05,0.05);
	return vec4(c.xyz,1.0);
}

void main()
{
	vec3 raydir = normalize(gl_TexCoord[1].xyz);
	vec4 intrpos = RayCast(gl_TexCoord[0].xyz, raydir);

	if(intrpos.w == 0.0)
	{
		//gl_FragColor = vec4(0,0,0,0);
		discard;
	}

	vec3 n = GetNormal(intrpos.xyz);
	gl_FragColor = CalculateLight(intrpos.xyz, n, raydir);
}
